namespace Microsoft.Robotics.Core.Algorithms.UnitTests
{
    using System;
    using Microsoft.Robotics.Numerics;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    /// <summary>
    /// Class for testing the P/R calculator
    /// </summary>
    [TestClass]
    public class PrecisionAndRecallCalculatorTests
    {
        /// <summary>
        /// Initial Values Are Zero When No Samples Are Added
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void InitialValuesAreZeroWhenNoSamplesAreAdded()
        {
            PRCalculator prc = new PRCalculator();

            Assert.AreEqual(0, prc.TruePositives.Mean);
            Assert.AreEqual(0, prc.FalsePositives.Mean);
            Assert.AreEqual(0, prc.TrueNegatives.Mean);
            Assert.AreEqual(0, prc.FalseNegatives.Mean);
            Assert.AreEqual(0, prc.RollingPrecision.Mean);
            Assert.AreEqual(0, prc.RollingRecall.Mean);
            Assert.AreEqual(0, prc.TotalPrecision);
            Assert.AreEqual(0, prc.TotalRecall);
            Assert.AreEqual(0, prc.TotalAccuracy);
            Assert.AreEqual(0, prc.TotalSpecificity);
        }

        /// <summary>
        /// Values are Updated When One Sample Is Added
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void ValuesUpdatedWhenOneSampleIsAdded()
        {
            PRCalculator prc = new PRCalculator();

            prc.AddMeasurement(1, 2, 3, 4);

            Assert.AreEqual(1, prc.TruePositives.Total);
            Assert.AreEqual(1, prc.TruePositives.Mean);
            Assert.AreEqual(2, prc.FalsePositives.Total);
            Assert.AreEqual(2, prc.FalsePositives.Mean);
            Assert.AreEqual(3, prc.TrueNegatives.Total);
            Assert.AreEqual(3, prc.TrueNegatives.Mean);
            Assert.AreEqual(4, prc.FalseNegatives.Total);
            Assert.AreEqual(4, prc.FalseNegatives.Mean);
            Assert.AreEqual(1.0 / (1 + 2), prc.RollingPrecision.Mean);
            Assert.AreEqual(1.0 / (1 + 2), prc.RollingPrecision.Last);
            Assert.AreEqual(1.0 / (1 + 4), prc.RollingRecall.Mean);
            Assert.AreEqual(1.0 / (1 + 4), prc.RollingRecall.Last);
            Assert.AreEqual(prc.RollingPrecision.Last, prc.TotalPrecision);
            Assert.AreEqual(prc.RollingRecall.Last, prc.TotalRecall);
            Assert.AreEqual(3.0 / (3 + 2), prc.TotalSpecificity);
            Assert.AreEqual((1.0 + 3.0) / (1 + 2 + 3 + 4), prc.TotalAccuracy);
        }

        /// <summary>
        /// Rolling Values Updated When Many Sample Are Added
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void RollingValuesUpdatedWhenManySampleAreAdded()
        {
            PRCalculator prc = new PRCalculator();

            prc.AddMeasurement(1, 2, 3, 4);
            prc.AddMeasurement(11, 22, 33, 44);
            prc.AddMeasurement(111, 222, 333, 444);
            prc.AddMeasurement(1111, 2222, 3333, 4444);

            double totalTP = 1 + 11 + 111 + 1111;
            double totalFP = 2 + 22 + 222 + 2222;
            double totalTN = 3 + 33 + 333 + 3333;
            double totalFN = 4 + 44 + 444 + 4444;

            Assert.AreEqual(totalTP, prc.TruePositives.Total);
            Assert.AreEqual(totalFP, prc.FalsePositives.Total);
            Assert.AreEqual(totalTN, prc.TrueNegatives.Total);
            Assert.AreEqual(totalFN, prc.FalseNegatives.Total);
            Assert.AreEqual(totalTP / (totalTP + totalFP), prc.TotalPrecision);
            Assert.AreEqual(totalTP / (totalTP + totalFN), prc.TotalRecall);
            Assert.AreEqual(prc.TotalPrecision, prc.RollingPrecision.Mean);
            Assert.AreEqual(prc.TotalRecall, prc.RollingRecall.Mean);

            Assert.AreEqual(totalTN / (totalTN + totalFP), prc.TotalSpecificity);
            Assert.AreEqual((totalTP + totalTN) / (totalTP + totalFP + totalTN + totalFN), prc.TotalAccuracy);
        }
    }
}
